<?php
defined('BASEPATH') or exit('No direct script access allowed');

/**
 * 用户模型
 */
class User_mdl extends MY_Model
{
    protected   $_table = parent::T_USER;
    /**
     * cookie键名
     * @var string
     */
    const COOKIE_KEY  = 'ys_auth';
    /**
     * cookie过期时间(秒)
     * @var int
     */
    const COOKIE_EXP  = 604800;
    static $db = NULL;

    public function __construct()
    {
        parent::__construct();
        $this->load->database('default');
        self::$db = &$this->db;
    }

    public function getUserWhereIn($list){
    	$res =  $this->db->select('id,nickname')
				    	 ->from(parent::T_USER)
				    	 ->where_in('id',$list)
				    	 ->get()
				    	 ->result_array();
    	return $res;
    }

    /**
     * 通过ID获取用户
     *
     * @param int       $userID
     * @return UserORM   用户对象
     */
    static  function findOneByID($userID)
    {
        return self::findOne(array('id' => $userID));
    }

    /**
     * 通过where条件获取一个用户对象
     *
     * @param array $where
     * @return UserORM   用户对象
     */
    static function findOne($where,$select='*')
    {
    	$user = self::$db->select($select)
                       ->from(parent::T_USER)
                       ->where($where)
                       ->limit(1)
                       ->get()
                       ->row_array();
    	return empty($user) ? FALSE : new UserORM($user,TRUE);
    }
    /**
     * 账户合并
     * @param array $mainUser 主账户数组
     * @param array $delUser  废弃账户数组
     *
	 * @abstract 涉及表:
	 * hs_order订单表
	 * hs_presrent赠送纪录表
	 * hs_recharge_order充值记录表
	 * hs_user_dev用户设备表
     */
    public function combine($mainUser,$delUser){
    	if ( $mainUser instanceof UserORM ) {
    		$mainUser = $mainUser->toArray();
    	}
    	if ( $delUser instanceof UserORM ) {
    		$delUser = $delUser->toArray();
    	}

    	$trans = array();// 存储此次更新事务的sql
		$userTable = MY_Model::T_USER;

		// 更新废弃账号为已删除
		$delSet = implode(',', array(
				'phone=NULL',
				'wxOpenID=NULL',
				'wxUnionID=NULL',
				'isDel=1',
		));
		$trans [] = "UPDATE {$userTable} SET {$delSet} WHERE id={$delUser['id']}";
		// 将废弃账号的数据更新到主账号
		$set = array();  // 存储用户表要更新的sql片段,
		$set[] = 'combineID='.$delUser['id'];
		$vals = array();
		foreach ( array('balance','rechargeTotal','buyTotal','presrent')  as $field){
			$val = $delUser[$field];
			if( empty($val) ) continue;
			$set[] = "{$field}={$field}+{$val}";
		}
		foreach( array('phone','wxOpenID','wxUnionID','nickname','headImgUrl','isTest','schoolID') as $field ){
			$val = empty($mainUser[$field]) ? $delUser[$field] : $mainUser[$field];
			if( empty($val) || $val == '0' ) continue;
			$set[] = "{$field}= ?";
			$vals[] = $val;
		}
		$set = implode(',', $set);
		$trans[] = array(
				'sql'=>"UPDATE {$userTable} SET {$set} WHERE id={$mainUser['id']}",
				'vals'=>$vals,
		);
		// 更新各个表中的数据
		$changes = array();
		$where = array('userID'=>$delUser['id']);
		foreach ( array(MY_Model::T_RECHARGE_ORDER,MY_Model::T_ORDER, MY_Model::T_PRESRENT) as $table ){
			$_list = $this->_getColList('id',$where,$table);
			if( empty($_list) ) continue;
			$changes[$table] = $_list;
			$trans[] = "UPDATE {$table} SET userID={$mainUser['id']} WHERE userID={$delUser['id']}";
		}
		// 开始更新事务处理
		$this->db->trans_strict ( FALSE );
		$this->db->trans_begin ();
		$res = FALSE;
		foreach( $trans as $sql ){
			if( is_array($sql) ){
				$res = $this->db->query($sql['sql'],$sql['vals']);
			}else{
				$res = $this->db->query($sql);
			}
			if( $this->db->trans_status() === FALSE ){
				debug('trans_status',false);
				$this->db->trans_rollback();
				return FALSE;
			}
		}
		// 成功之后纪录此次改变到mongo
		if( ! empty($res) ){
			$this->load->model('User_combine_log_mdl','combineLog');
			$res = $this->combineLog->recode($mainUser,$delUser,$changes);
		}
		if( empty($res) ){
			$this->db->trans_rollback();
			return FALSE;
		}
		$this->db->trans_commit();

		$user = $this->findOneByID($mainUser['id']);
		return $user;
	}
	/**
	 * 获取数据表中一列的数据
	 * @param string $field 列字段名
	 * @param array $where
	 * @param string $table
	 */
	private function _getColList($field='id',$where='',$table=''){
		$list = $this->db->select($field)->from($table)->where($where)->get()->result_array();
		return array_column($list, $field);
	}

   /**
    * 创建一个新用户
    * @param int|array $user	用户ID或者用户数据数组
    * @param bool $isLoaded		是否已从数据库加载对象
    * @return UserORM			用户对象
    */
    static function create($user=NULL,$isLoaded=FALSE)
    {
        return new UserORM($user,$isLoaded);
    }


    /**
     * 获取当前登录用户
     *
     * @return UserORM   用户对象
     */
    static function getLoginUser(){
    	static $user = NULL;
    	if( NULL !== $user ){
    		return $user;
    	}
		if ( empty($_COOKIE[self::COOKIE_KEY]) )  return FALSE;
		$auth = $_COOKIE[self::COOKIE_KEY];
		$auth = UserAuth::decode($auth);
		if( !$auth ) return FALSE;
		$auth = explode('$$$', $auth);
		if( empty($auth[1]) || !is_numeric($auth[0]) ) return FALSE;
		$uid = $auth[0];
		$phoneID = $auth[1];
		if (empty($uid) || empty($phoneID)) return FALSE;
		$user = new UserORM($uid);
		$user->currentPhoneID = $phoneID;
		return $user;
    }

    public function syncSchoolID(){
    	$sql = "UPDATE 
					`hs_user` AS u,
					`hs_order` AS o,
					`hs_dev` AS d
				SET 
					u.`schoolID` = d.`schoolID`
				WHERE
							u.`schoolID` = 0
					AND 	o.`userID`   = u.`id`
					AND 	o.`devID`    = d.`id`";
    	return $this->db->query($sql);
    }
    /**
     * 用户买水扣钱
     * @param int $uid
     * @param int $amount
     */
    public function buy($uid,$amount){
    	return $this->db->set('balance','balance - '.$amount,FALSE)
		    			->set('buyTotal','buyTotal + '.$amount,FALSE)
		    			->where('id',$uid)
		    			->update($this->_table());
    }
    /**
     * 离线授权买水
     * @param UserORM $user
     * @param int $amount
     */
    public function offlineBuy($user,$amount){
    	if( is_numeric($user) ){
    		$user = self::findOneByID($user);
    	}



    }

    /**
     * 用户充值
     * @param int $uid
     * @param int $amount
     */
    public function recharge($uid,$amount){
    	return $this->db->set('balance','balance + '.$amount,FALSE)
			    	->set('rechargeTotal','rechargeTotal + '.$amount,FALSE)
			    	->where('id',$uid)
			    	->update($this->_table());
    }





}

/**
 * 用户对象
 * @property int $id
 * @property string $phone
 * @property string $password
 * @property string $wxOpenID
 * @property string $wxUnionID
 * @property string $phoneID
 * @property string $nickname
 * @property int  $balance
 * @property int    $regTime
 * @property string $headImgUrl
 * @property int $isTest
 * @property int $schoolID
 * @property int $prepayment 离线授权余额
 * @property string $lockedPhoneID
 */
class UserORM{
	private $ci = NULL;
	private $db = NULL;
	/**
	 * 用户ID
	 */
	private $id = 0;
	/**
	 * 是否已从数据库加载过对象
	 */
	private $isLoaded = FALSE;
	/**
	 * 用户数据
	 */
	public $_user = array(
			'phone'=>'',
			'password'=>'',
			'wxOpenID'=>'',
			'wxUnionID'=>'',
			'phoneID'=>'',
			'balance'=>0,
			'nickname'=>'',
			'regTime'=>'',
			'headImgUrl'=>'',
			'isTest'=>0,
			'rechargeTotal'=>0,
			'buyTotal'=>0,
			'presrent'=>0,
			'schoolID'=>0,
			'prepayment'=>0,
			'lockedPhoneID'=>NULL,
	);
	/**
	 * 纪录属性的初始值
	 * @var array
	 */
	private $_initial = NULL;

	private $_isLoadedDB = FALSE;

	/**
	 * 当前使用的手机的phoneID
	 * @var string
	 */
	public  $currentPhoneID = NULL;
    /**
     * 用户对象
     * @param int|array $user 用户ID或用户数据数组
     * @param bool $isLoaded 是否是从数据库加载的最新数据
     */
	public function __construct($user=NULL,$isLoaded = FALSE){
		$this->_setUser($user);
		$this->_init();
		$this->isLoaded = $isLoaded;
	}
	private function _init(){
		$this->ci = &get_instance();
	}
	private function _setUser($user){
	    if( is_numeric($user) ){
	        $this->id = $user;
	    }else if( is_array($user) ){
	        if (isset($user['id'])){
	            $this->id = $user['id'];
	        }
	        foreach ($user as $k=>$v){
	        	$this->_user[$k] = $v;
	        }
	    }
	}
	private function _loadDB(){
		// 因为CI的db对象默认采用单例模式
		// 为避免实例化多个user对象时AR模式错乱
		// 因而当set操作时,为每个user对象建立一个私有的db对象
		if( $this->_isLoadedDB ) return TRUE;
		$this->db = $this->ci->load->database('default',TRUE);
		$this->_isLoadedDB = TRUE;
// 		debug('userDB'.$this->id,$this->db);
	}
	public function __get($key){
		if( $key=='id' ){
			return $this->id;
		}
		if ( !$this->isLoaded  ){
			$this->load();
		}
		if( array_key_exists($key, $this->_user) ){
			return $this->_user[$key];
		}
	}
	public function __set($key,$val){
		debug('user['.$this->id.']->'.$key,$val);
		if( !$this->_isLoadedDB ){
			$this->_loadDB();
		}
		switch ($key){
		    // ==== 数值型字段的处理 ========
			case 'balance':
			case 'prepayment':
			    if ( !isset($this->_initial[$key]) ) {
			        $this->_initial[$key] = $this->$key;
			    }
				//以上字段均为无符号整型,不能设为负数
				if ($val<=0) {
				    // TODO 当小于0的时候是否触发报错?
					$val = 0;
					$this->db->set($key,0);
				}else{
				    $diff = $val - $this->_initial[$key];
				    if( $diff != 0  ){
				        $this->db->set($key,$key.' + '.$diff,FALSE);
				    }
				}
				$this->_user[$key] = $val;
				break;

			// ===== 字符串型字段的处理 =========
			default :
				if ( array_key_exists($key, $this->_user) ) {
					$this->db->set($key,$val);
					$this->_user[$key] = $val;
				}
				break;
		}
	}
	public function __isset($key){
		return $key == 'id' OR isset($this->_user[$key]) ;
	}

	/**
	 * 从数据库加载对象
	 * @return boolean|UserORM
	 */
	public function load(){
		if( TRUE===$this->isLoaded ){
			return $this;
		}
		foreach (array('id','phone','wxOpenID','wxUnionID') as $key){
			if ( empty($this->$key) ) {
				continue;
			}
			$val = $this->$key;
			if( !empty($val) ){
				$user = User_mdl::$db->where($key,$val)->from(MY_Model::T_USER)->limit(1)->get()->row_array();
				if( empty($user) ) return FALSE;
				$this->_setUser($user);
				$this->isLoaded = TRUE;
				return $this;
				break;
			}
		}
		return FALSE;
	}
	/**
	 * 将用户插入到数据库
	 * @return UserORM|boolean
	 */
	public function reg(){
		$this->isLoaded = TRUE;
		$this->regTime = time();
		if( ! $this->db->insert(MY_Model::T_USER) ){
			return FALSE;
		}
		$this->id = $this->db->insert_id();
		$this->afterLogin($this->phoneID);
		return $this;
	}
	/**
	 * 用户登录后操作
	 */
	public function afterLogin($phoneID){
	    $this->currentPhoneID = $phoneID;
	    // 设置cookie
		$info = $this->id.'$$$'.$phoneID;
		$info = UserAuth::encode($info);
		setcookie(User_mdl::COOKIE_KEY,$info,time()+User_mdl::COOKIE_EXP,'/');
		// 更新最后一次登录的phoneID到数据库
		UserLoginLog::log($this->id, $phoneID);
	}
	public function logout(){
		setcookie(User_mdl::COOKIE_KEY,'',-1,'/');
	}

	const PASSWORD_SALT = 'yx&rj#$';//密码盐值

	public function checkPassword($password){
		return md5(self::PASSWORD_SALT.$password) == $this->password;
	}

	public function setPassword($password,$needUpdate=FALSE){
		$this->password = md5(self::PASSWORD_SALT.$password);
		if( $needUpdate ){
			return $this->update();
		}
		return TRUE;
	}
	/**
	 * 是否绑定手机
	 */
	public function isBindPhone(){
		return !empty($this->phone);
	}
	/**
	 * 充值卡充值
	 * @param int $amount
	 * @return boolean
	 */
	public function rechargeByCard($amount){
		$this->balance+=$amount;
		$this->rechargeCardTotal+=$amount;
		return $this->update();
	}


	/**
	 * 更新用户信息到数据库
	 * @return boolean
	 */
	public function update(){
		return $this->db->where('id',$this->id)->update(MY_Model::T_USER);
	}
	/**
	 * 获取用户基本信息
	 * @return array
	 */
	public function getBaseInfo(){
		$headImgUrl = $this->headImgUrl;
		if (!empty($headImgUrl)) {
			if (!preg_match('/^https?:\/\//',$headImgUrl)) {
				$headImgUrl = base_url($headImgUrl);
			}
		}
		return array(
				'id' => ( int ) $this->id,
				'phone' => ( string ) $this->phone,
				'wxOpenID' => ( string ) $this->wxOpenID,
				'nickname' => ( string ) $this->nickname,
				'headImgUrl' => ( string ) $headImgUrl,
				'balance' => ( int ) $this->balance,
				'isTest' => ( int ) $this->isTest,
				// V2新增字段
				'prepayment' => ( int ) $this->prepayment,
				'issetPwd' => ( int ) ! empty ( $this->password ),
				'isbindPhone' => ( int ) ! empty ( $this->phone ),
				'isbindWx' => ( int ) ! empty ( $this->wxOpenID ),
				'issetSchool'=>(int) ! empty($this->schoolID),
		);
	}
	public function toArray(){
		$rtn = $this->_user;
		$rtn['id'] = $this->id;
		return $rtn;
	}

	/**
	 * 检查最后一次登录的手机与本次操作使用的手机的phoneID是否一致
	 * @return boolean
	 */
	public function checkLastLoginPhoneID(){
		$lastLoginPhoneID = UserLoginLog::getLastLoginPhoneID($this->id);
		return strcmp( $lastLoginPhoneID , trim($this->currentPhoneID) ) === 0;
	}

	public function __destruct(){
		if (!empty($this->_update)){
			debug($this->id,$this);
			UserCache::save($this->id, $this);
		}
	}
	public function __sleep(){
		return array('id','_user');
	}
	public function __wakeup(){
		$this->_init();
	}
}

class UserLoginLog{
	static function log($userID,$phoneID){
		$CI = &get_instance();
		$data = array (
				'userID' => $userID,
				'phoneID' => $phoneID,
				'time' => time (),
				'ip' => sprintf ( "%u", ip2long ( $CI->input->ip_address() ) )
		);
		return User_mdl::$db->insert ( MY_Model::T_USER_LOGIN_LOG, $data );
	}
	static function getLastLoginPhoneID($userID){
		$result = User_mdl::$db->select('phoneID')
								->from(MY_Model::T_USER_LOGIN_LOG)
								->where('userID',$userID)
								->order_by('id','DESC')
								->limit(1)
								->get()
								->row_array();
		return isset($result['phoneID']) ? $result['phoneID'] : NULL;
	}
}

class UserAuth{
	const KEY = 'yx_yinshui_app_auth_code_user_mdl_!@#$%^~()*^%$sdkfnsjs';
	const EXP = 0;//有效期
	static function encode($str){
		return authcode($str,'ENCODE',self::KEY,self::EXP);
	}
	static function decode($str){
		return authcode($str,'DECODE',self::KEY,self::EXP);
	}
}
class UserCache{
	static $prefix = 'user_';
	static $cache  = NULL;
	static function getCacheIns(){
		if( NULL === self::$cache ){
			$CI = &get_instance();
			$CI->load->driver('cache',array('adapter'=>'file'));
			self::$cache = $CI->cache;
		}
	}
	static function save($key,$data){
		self::getCacheIns();
		return self::$cache->save(self::$prefix.$key,$data);
	}
	static function get($key){
		self::getCacheIns();
		return self::$cache->get(self::$prefix.$key);
	}
	static function delete($key){
		self::getCacheIns();
		return self::$cache->delete(self::$prefix.$key);
	}
}
